package com.bjbn.service.impl;

import cn.hutool.core.util.HexUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import com.bjbn.mapper.FidsT3DeviceMapper;
import com.bjbn.mapper.FidsT3DevicePictureMapper;
import com.bjbn.model.entity.FidsT3Device;
import com.bjbn.model.entity.FidsT3DevicePicture;
import com.bjbn.service.FidsT3DeviceService;
import com.bjbn.service.KafkaService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * T3航显屏设备服务实现类
 */
@Slf4j
@Service
public class FidsT3DeviceServiceImpl implements FidsT3DeviceService {

    @Autowired
    private FidsT3DeviceMapper deviceMapper;

    @Autowired
    private FidsT3DevicePictureMapper pictureMapper;

    @Autowired
    private KafkaService kafkaService;

    @Value("${api.device.list-url:http://10.120.79.48:8081/t3fids-web-boss/device/info/api/listAllDevice.do}")
    private String deviceListUrl;

    @Value("${api.screenshot.url:http://10.120.79.120:3000/api/url2image}")
    private String screenshotUrl;

    @Value("${screenshot.image-quality:40}")
    private Integer imageQuality;

    @Value("${screenshot.timeout:15000}")
    private int HTTP_TIMEOUT;

    @Value("${screenshot.retry.max-attempts:2}")
    private int MAX_RETRIES;

    @Value("${distributed.enabled:false}")
    private boolean distributedEnabled;

    @Value("${distributed.instance.id:0}")
    private int instanceId;

    @Value("${distributed.instance.total:1}")
    private int totalInstances;

    // 线程池配置 - 增加线程数量以支持946块屏幕的并发处理
    // 采用CPU核心数的8倍，并设置最小40个线程，确保有足够的并发能力
    private final ExecutorService processExecutor = Executors.newFixedThreadPool(
            Math.max(Runtime.getRuntime().availableProcessors() * 8, 40));

    // 性能监控指标
    private static final AtomicLong successCount = new AtomicLong(0);
    private static final AtomicLong failureCount = new AtomicLong(0);

    @Override
    public void syncDeviceList() {
        try {
            log.info("开始同步航显屏设备列表...");

            // 调用接口获取设备列表，设置字符编码为UTF-8
            HttpResponse response = HttpRequest.post(deviceListUrl)
                    .header(Header.HOST, "10.120.79.48:8081")
                    .header(Header.USER_AGENT, "curl/7.71.1")
                    .header(Header.ACCEPT, "*/*")
                    .header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=UTF-8")
                    .header("Accept-Encoding", "identity")
                    .timeout(HTTP_TIMEOUT)
                    .charset("UTF-8")
                    .execute();

            if (response.getStatus() != 200) {
                log.error("获取设备列表失败，状态码: {}", response.getStatus());
                return;
            }

            // 显式获取响应体字节并转换为UTF-8字符串
            byte[] bodyBytes = response.bodyBytes();
            String responseBody = new String(bodyBytes, StandardCharsets.UTF_8);
            // fastjson2直接解析UTF-8字符串
            JSONObject responseJson = JSON.parseObject(responseBody);
            String head = responseJson.getString("head");

            if ("success".equals(head)) {
                JSONArray body = responseJson.getJSONArray("body");
                List<FidsT3Device> devices = new ArrayList<>();

                for (Object obj : body) {
                    JSONObject deviceJson = (JSONObject) obj;
                    FidsT3Device device = FidsT3Device.builder()
                            .id(deviceJson.getString("id"))
                            .name(deviceJson.getString("name"))
                            .deviceIp(deviceJson.getString("deviceIp"))
                            .contentUrl(deviceJson.getString("contenturl"))
                            .mac(deviceJson.getString("mac"))
                            .os(deviceJson.getString("os"))
                            .typeid(deviceJson.getString("typeid"))
                            .groupid(deviceJson.getString("groupid"))
                            .domainid(deviceJson.getString("domainid"))
                            .isdelete(deviceJson.getInteger("isdelete"))
                            .orderno(deviceJson.getInteger("orderno"))
                            .version(deviceJson.getInteger("version"))
                            .isusing(1) // 默认启用
                            .createTime(new Date())
                            .updateTime(new Date())
                            .build();
                    devices.add(device);
                }

                // 批量插入或更新设备信息
                if (!devices.isEmpty()) {
                    deviceMapper.batchInsertOrUpdate(devices);
                    log.info("设备列表同步成功，共 {} 条记录", devices.size());
                }
            } else {
                log.error("获取设备列表失败，响应头: {}", head);
            }
        } catch (Exception e) {
            log.error("同步设备列表异常", e);
        }
    }

    @Override
    public List<FidsT3Device> getEnabledDevices() {
        return deviceMapper.selectEnabledDevices();
    }

    @Override
    public String captureDeviceScreen(FidsT3Device device) {
        return fetchImageWithRetry(device);
    }

    @Override
    public void updateDevicePicture(String deviceId, String pictureData) {
        try {
            Date now = new Date();
            FidsT3DevicePicture picture = FidsT3DevicePicture.builder()
                    .id(deviceId)
                    .picture(pictureData)
                    .createTime(now)
                    .updateTime(now)
                    .build();
            pictureMapper.updateDevicePicture(picture);
            
            // 发送数据到Kafka
            kafkaService.sendFidsScreenData(deviceId, pictureData, now);
        } catch (Exception e) {
            log.error("更新设备截图失败，设备ID: {}", deviceId, e);
            throw new RuntimeException("更新设备截图失败", e);
        }
    }

    @Override
    public void batchCaptureScreens() {
        long startTime = System.currentTimeMillis();
        try {
            log.info("开始批量截图...");
            // 重置计数
            successCount.set(0);
            failureCount.set(0);

            // 检查截图服务是否可用
            /*
            检查服务状态暂不可行，接口不支持
            boolean screenshotServiceAvailable = checkScreenshotServiceAvailability();
            if (!screenshotServiceAvailable) {
                log.error("截图服务不可用，请检查服务状态: {}", screenshotUrl);
                return;
            }
            log.info("截图服务状态检查通过");
            */

            // 获取所有启用的设备
            List<FidsT3Device> devices = getEnabledDevices();
            log.info("待截图设备数量: {}, 线程池大小: {}",
                    devices.size(), ((ThreadPoolExecutor) processExecutor).getCorePoolSize());

            // 性能优化：预分片处理，支持分布式部署场景
            List<FidsT3Device> assignedDevices = getAssignedDevicesForCurrentInstance(devices);
            log.info("当前实例分配设备数量: {}", assignedDevices.size());

            int totalDevices = assignedDevices.size();
            if (totalDevices == 0) {
                log.info("没有设备需要处理，任务结束");
                return;
            }

            // 批量处理配置
            int batchSize = 50; // 减小批次大小以提高响应速度

            log.info("开始分批处理截图任务，每批 {} 个设备，预计 {} 批",
                    batchSize, (int) Math.ceil((double) totalDevices / batchSize));

            // 分批处理，避免一次性创建太多任务
            for (int i = 0; i < totalDevices; i += batchSize) {
                int end = Math.min(i + batchSize, totalDevices);
                List<FidsT3Device> batchDevices = assignedDevices.subList(i, end);

                log.info("处理批次 {}/{}，设备数量: {}",
                        (i / batchSize) + 1,
                        (int) Math.ceil((double) totalDevices / batchSize),
                        batchDevices.size());

                // 为当前批次创建任务
                List<CompletableFuture<Void>> batchFutures = new ArrayList<>();
                for (FidsT3Device device : batchDevices) {
                    batchFutures.add(CompletableFuture.runAsync(() -> {
                        try {
                            // 添加设备URL有效性检查
                            if (isValidUrl(device.getContentUrl())) {
                                processSingleDevice(device);
                                successCount.incrementAndGet();
                                log.debug("设备截图成功，设备ID: {}", device.getId());
                            } else {
                                failureCount.incrementAndGet();
                                log.warn("设备URL无效，跳过截图，设备ID: {}, URL: {}",
                                        device.getId(), device.getContentUrl());
                            }
                        } catch (Exception e) {
                            failureCount.incrementAndGet();
                            // 详细记录异常信息
                            String errorMsg = e.getMessage();
                            log.error("设备截图失败，设备ID: {}, 设备名称: {}, 错误信息: {}",
                                    device.getId(), device.getName(), errorMsg);

                            // 统计常见错误类型
                            if (errorMsg.contains("timeout") || errorMsg.contains("超时")) {
                                log.warn("截图超时，可能需要增加超时时间或优化截图服务");
                            }
                        }
                    }, processExecutor));
                }

                // 减小超时时间，避免单批次阻塞太久
                try {
                    CompletableFuture.allOf(batchFutures.toArray(new CompletableFuture[0]))
                            .get(180, TimeUnit.SECONDS); // 3分钟超时
                } catch (TimeoutException e) {
                    log.error("批次处理超时，部分设备截图可能未完成");
                    // 不中断执行，继续处理下一批次
                }

                // 记录批次完成状态
                long currentBatchTime = System.currentTimeMillis() - startTime;
                log.info("批次 {}/{} 完成，累计处理: {}, 成功: {}, 失败: {}, 耗时: {} 秒",
                        (i / batchSize) + 1,
                        (int) Math.ceil((double) totalDevices / batchSize),
                        successCount.get() + failureCount.get(),
                        successCount.get(),
                        failureCount.get(),
                        String.format("%.2f", currentBatchTime / 1000.0));

                // 增加批次间短暂休息，避免对截图服务持续高压
                Thread.sleep(1000);
            }

            long endTime = System.currentTimeMillis();
            long totalTimeMs = endTime - startTime;
            double totalTimeSeconds = totalTimeMs / 1000.0;
            double totalTimeMinutes = totalTimeSeconds / 60.0;

            // 计算平均处理速度
            double averageTimePerDevice = 0;
            double devicesPerSecond = 0;
            if (successCount.get() > 0) {
                averageTimePerDevice = totalTimeMs / (double) successCount.get();
                devicesPerSecond = successCount.get() / totalTimeSeconds;
            }

            log.info("批量截图完成 - 成功: {}, 失败: {}", successCount.get(), failureCount.get());
            log.info("总耗时: {} 毫秒 ({} 秒, {} 分钟)",
                    totalTimeMs, String.format("%.2f", totalTimeSeconds), String.format("%.2f", totalTimeMinutes));
            log.info("平均每设备处理时间: {} 毫秒", String.format("%.2f", averageTimePerDevice));
            log.info("处理速度: {} 设备/秒", String.format("%.2f", devicesPerSecond));
            log.info("是否在5分钟内完成: {}", totalTimeMinutes < 5.0 ? "是" : "否");
        } catch (Exception e) {
            log.error("批量截图异常", e);
        }
    }

    private void processSingleDevice(FidsT3Device device) {
        if (device == null || device.getContentUrl() == null) {
            return;
        }
        // 截取屏幕
        String imageData = captureDeviceScreen(device);
        // 更新截图信息
        updateDevicePicture(device.getId(), imageData);
    }

    private String fetchImageWithRetry(FidsT3Device device) {
        for (int retry = 0; retry < MAX_RETRIES; retry++) {
            try {
                return fetchImageData(device);
            } catch (Exception e) {
                if (retry == MAX_RETRIES - 1) {
                    log.error("截图失败，已达最大重试次数，设备ID: {}", device.getId(), e);
                    throw new RuntimeException("截图失败", e);
                }
                log.warn("截图失败，正在重试 ({}次/{}次)，设备ID: {}",
                        retry + 1, MAX_RETRIES, device.getId());
                sleepExponentialBackoff(retry);
            }
        }
        return "";
    }

    /**
     * 检查截图服务是否可用
     */
    private boolean checkScreenshotServiceAvailability() {
        try {
            log.info("检查截图服务可用性: {}", screenshotUrl);
            // 发送一个简单的HEAD请求检查服务状态
            HttpResponse response = HttpRequest.head(screenshotUrl)
                    .timeout(5000) // 5秒超时
                    .execute();
            return response.getStatus() >= 200 && response.getStatus() < 400;
        } catch (Exception e) {
            log.error("截图服务检查失败", e);
            return false;
        }
    }

    /**
     * 验证URL是否有效
     */
    private boolean isValidUrl(String url) {
        if (url == null || url.trim().isEmpty()) {
            return false;
        }
        try {
            new URL(url.trim());
            return true;
        } catch (MalformedURLException e) {
            return false;
        }
    }

    /**
     * 获取当前实例分配的设备列表（支持分布式部署）
     */
    private List<FidsT3Device> getAssignedDevicesForCurrentInstance(List<FidsT3Device> allDevices) {
        // 优先使用系统属性（便于容器化部署），其次使用配置文件
        int effectiveInstanceId = Integer.parseInt(
                System.getProperty("instance.id", String.valueOf(instanceId)));
        int effectiveTotalInstances = Integer.parseInt(
                System.getProperty("instance.total", String.valueOf(totalInstances)));

        // 只有在分布式模式启用且总实例数大于1时才进行分片
        if (!distributedEnabled || effectiveTotalInstances <= 1) {
            return allDevices; // 单实例模式，处理所有设备
        }

        log.info("分布式模式 - 当前实例ID: {}, 总实例数: {}, 设备总数: {}",
                effectiveInstanceId, effectiveTotalInstances, allDevices.size());

        // 按设备ID哈希分片
        List<FidsT3Device> assignedDevices = new ArrayList<>();
        for (FidsT3Device device : allDevices) {
            int deviceHash = Math.abs(device.getId().hashCode());
            if (deviceHash % effectiveTotalInstances == effectiveInstanceId) {
                assignedDevices.add(device);
            }
        }

        log.info("当前实例分配到 {} 台设备进行截图处理", assignedDevices.size());
        return assignedDevices;
    }

    private String fetchImageData(FidsT3Device device) {
        log.debug("开始截图，设备ID: {}, 设备名称: {}, URL: {}",
                device.getId(), device.getName(), device.getContentUrl());

        // 构建截图请求参数
        JSONObject requestBody = new JSONObject();
        requestBody.put("url", device.getContentUrl().trim()); // 去除首尾空格
        requestBody.put("width", "1920");
        requestBody.put("height", "1080");
        requestBody.put("fullPage", false);
        requestBody.put("autoSize", false);
        requestBody.put("imageFormat", "jpeg");
        requestBody.put("imageQuality", imageQuality);

        // 增加超时设置，根据设备数量动态调整
        int dynamicTimeout = Math.min(HTTP_TIMEOUT * 2, 30000); // 最长30秒

        log.debug("截图请求参数: {}, 超时设置: {}ms", requestBody.toJSONString(), dynamicTimeout);

        // 调用截图服务，设置字符编码为UTF-8
        HttpResponse response = HttpRequest.post(screenshotUrl)
                .header("Content-Type", "application/json; charset=UTF-8")
                .body(requestBody.toJSONString())
                .timeout(dynamicTimeout)
                .charset("UTF-8")
                .execute();

        int statusCode = response.getStatus();
        if (statusCode != 200) {
            String errorMessage = String.format("截图HTTP请求失败，状态码: %d, 响应体: %s",
                    statusCode, response.body());
            log.error(errorMessage + ", 设备ID: {}", device.getId());
            throw new RuntimeException(errorMessage);
        }

        // 解析响应，确保使用UTF-8编码
        byte[] responseBytes = response.bodyBytes();
        String responseBody = new String(responseBytes, java.nio.charset.StandardCharsets.UTF_8);

        // 检查响应体是否为空
        if (responseBody == null || responseBody.trim().isEmpty()) {
            throw new RuntimeException("截图服务返回空响应");
        }

        try {
            JSONObject responseJson = JSON.parseObject(responseBody);
            // 检查是否包含必要的字段
            if (!responseJson.containsKey("imageBase64WithPrefix")) {
                throw new RuntimeException("截图响应缺少必要字段: imageBase64WithPrefix");
            }

            String imageData = responseJson.getString("imageBase64WithPrefix");
            if (imageData == null || imageData.isEmpty()) {
                throw new RuntimeException("截图数据为空");
            }

            return imageData;
        } catch (JSONException e) {
            throw new RuntimeException("解析截图响应失败: " + responseBody, e);
        }
    }

    private void sleepExponentialBackoff(int retryCount) {
        try {
            long sleepTime = 1000L * (1 << retryCount); // 指数退避
            log.debug("等待 {}ms 后重试", sleepTime);
            Thread.sleep(sleepTime);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
    }

    public AtomicLong getSuccessCount() {
        return successCount;
    }

    public AtomicLong getFailureCount() {
        return failureCount;
    }

}